/***
 * 组合模式： 又叫“整体部分模式”。 将对象组合成树形结构，以表示“部分-整体
 *           的层次结构。
 * 
 *          通过对象的多态性表现，使得用户对单个对象和组合对象的使用具有一致性。
 * 
 *          生活小例子： 文件目录  DOM文档树
 * 
 *  模式的特点： 
 *      1、表示“部分-整体”的层次结构，生成“树叶型结构。
 *      2、一致操作性，树叶对象对外接口保存一致（操作与数据结构一致）；
 *      3、自上而下的请求流向，从树对象传递给叶对象。
 *      4、调用顶层对象，会自行遍历其下的叶对象执行。
 * 
 */

/**
  *  透明性的安全问题
  * 
  *    组合模式的透明性，指的是树叶对象接口保持一致，外部调用是无需区分。
  *    但是这会带来一些问题，如上述文件目录的例子，文件（叶对象）下不可再添加文件
  *    因此需要在文件类的add()方法中抛出异常，以作提醒。
  */
class CFolder { 
    constructor(name) { 
        this.name = name
        this.files = []
    }

    add(file) { 
        this.files.push(file)
    }

    scan() { 
        for (const file of this.files) {
            file.scan()
        }
    }
}

class CFile { 
    constructor(name) { 
        this.name = name;
    }

    add(file) { 
        throw new Error("文件下面不能再添加文件")
    }

    scan() { 
        console.log(`开始扫描文件：${this.name}`)
    }
}

let mediaFolder = new CFolder("娱乐")

let movieFolder = new CFolder("电影")
let musicFolder = new CFolder("音乐")

let file1 = new CFile("钢铁侠.mp4")
let file2 = new CFile("在谈记忆.mp3")
movieFolder.add(file1)
movieFolder.add(file2)
mediaFolder.add(movieFolder)
mediaFolder.add(musicFolder)
mediaFolder.scan()
//输出： 开始扫描文件：钢铁侠.mp4
//       开始扫描文件：在谈记忆.mp3
/**
 * 误区规避
 *  1、组合不是继承，树叶对象并不是父子对象。
 * 
 *    组合模式的树形结构是一种HAS-A（聚合）的关系，而不是IS-A。
 * 
 *  2、叶对象操作保持一致性。
 *  3、叶对象实现冒泡传递。
 *  4、不只是简单的子集遍历。
 */

/**
 * 优缺：
 *   1、忽略组合对象和单个对象的差别，对外一直接口使用。
 *   2、解耦调用者与复杂元素之间的联系，处理方式变得简单。
 * 
 * 缺点：
 *   1、树叶对象接口一致，无法区分只有在运行时方可辨别。
 *   2、包裹对象创建太多，额外增加内存负担。
 */